/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.xeerkat.xmpp.server.db;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.logging.Logger;
import org.milowski.db.DB;
import org.milowski.db.DBConnection;
import org.milowski.db.DBIterator;
import org.milowski.db.DBObject;
import org.milowski.db.DBQueryHandler;
import org.milowski.db.DBResultConstructor;
import org.milowski.db.DBUpdateHandler;
import org.milowski.db.Slot;
import org.xeerkat.xmpp.ID;

/**
 *
 * @author alex
 */
public class User extends DBObject<XMPPDB> {

   static Logger LOG = Logger.getLogger(User.class.getName());
   
   ID xmppid;
   public User(XMPPDB db,int id,ID xmppid)
   {
      super(db,id);
      this.xmppid = xmppid;
   }
   
   public ID getXMPPId() {
      return xmppid;
   }
   
   public boolean delete()
      throws SQLException
   {
      DBConnection connection = db.getConnection();
      try {
         connection.deleteById(XMPPDB.STATEMENT_DELETE_USER_CONTACT_GROUPS, id);
         connection.deleteById(XMPPDB.STATEMENT_DELETE_USER_GROUPS, id);
         connection.deleteById(XMPPDB.STATEMENT_DELETE_USER_CONTACTS, id);
         return connection.deleteById(XMPPDB.STATEMENT_DELETE_USER, id)>0;
      } finally {
         db.release(connection);
      }
   }
   
   public boolean hasContact(final ID contactId)
      throws SQLException
   {
      final Slot<Boolean> exists = new Slot<Boolean>(false);
      DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_GET_CONTACT, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setString(2,contactId.toXMPPId());
            }
            public void onResults(ResultSet set)
               throws SQLException
            {
               exists.set(set.next());  
            }
         });
      } finally {
         db.release(connection);
      }
      return exists.get();
   }
   
   public Contact addContact(final ID contactId)
      throws SQLException
   {
      if (!hasContact(contactId)) {
         DBConnection connection = db.getConnection();
         try {
            int dbid = connection.create(XMPPDB.STATEMENT_CREATE_USER_CONTACT, XMPPDB.STATEMENT_LAST_ID_FROM_CONTACTS, new DBUpdateHandler() {
               public void prepare(PreparedStatement s)
                  throws SQLException
               {
                  s.setInt(1,id);
                  s.setString(2,contactId.toXMPPId());
                  s.setInt(3,Contact.Subscription.None.getValue());
                  s.setInt(4,Contact.Ask.None.getValue());
               }
            });
            return new Contact(db,dbid,this,new ID(contactId.toXMPPId()),Contact.Subscription.None,Contact.Ask.None,null);
         } finally {
            db.release(connection);
         }
      } else {
         return null;
      }
   }
   
   public Contact getContact(final ID contactId)
      throws SQLException
   {
      final Slot<Contact> contact = new Slot<Contact>();
      DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_GET_CONTACT, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setString(2,contactId.toXMPPId());
            }
            public void onResults(ResultSet set)
               throws SQLException
            {
               if (set.next()) {
                  contact.set(new Contact(db,set.getInt(1),User.this,new ID(contactId.toXMPPId()),Contact.Subscription.fromInt(set.getInt(3)),Contact.Ask.fromInt(set.getInt(4)),set.getString(5)));
               }
            }
         });
      } finally {
         db.release(connection);
      }
      return contact.get();
   }

   public boolean hasGroup(final String name)
      throws SQLException
   {
      final Slot<Boolean> exists = new Slot<Boolean>(false);
      DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_GET_USER_GROUP, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setString(2,name);
            }
            public void onResults(ResultSet set)
               throws SQLException
            {
               exists.set(set.next());  
            }
         });
      } finally {
         db.release(connection);
      }
      return exists.get();
   }
   
   public Group addGroup(final String name)
      throws SQLException
   {
      if (!hasGroup(name)) {
         DBConnection connection = db.getConnection();
         try {
            int dbid = connection.create(XMPPDB.STATEMENT_CREATE_USER_GROUP, XMPPDB.STATEMENT_LAST_ID_FROM_GROUPS, new DBUpdateHandler() {
               public void prepare(PreparedStatement s)
                  throws SQLException
               {
                  s.setInt(1,id);
                  s.setString(2,name);
               }
            });
            return new Group(db,dbid,this,name);
         } finally {
            db.release(connection);
         }
      } else {
         return null;
      }
   }
   
   public Group getGroup(final String name)
      throws SQLException
   {
      final Slot<Group> group = new Slot<Group>();
      DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_GET_USER_GROUP, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setString(2,name);
            }
            public void onResults(ResultSet set)
               throws SQLException
            {
               if (set.next()) {
                  group.set(new Group(db,set.getInt(1),User.this,name));
               }
            }
         });
      } finally {
         db.release(connection);
      }
      return group.get();
   }
   
   public void cleanup() 
      throws SQLException
   {
      DBConnection connection = db.getConnection();
      try {
         connection.update(XMPPDB.STATEMENT_DELETE_EMPTY_USER_GROUPS, new DBUpdateHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1, id);
            }
         });
      } finally {
         db.release(connection);
      }
   }
   
   public ID goOnline(String resource)
      throws SQLException
   {
      String resourceId = xmppid.toXMPPId()+"/"+resource;
      String currentId = resourceId;
      int index = 1;
      while (db.isOnline(new ID(currentId))) {
         index++;
         currentId = resourceId+"-"+index;
      }
      final String resultId = currentId;
      DBConnection connection = db.getConnection();
      try {
         connection.create(XMPPDB.STATEMENT_GO_ONLINE, -1, new DBUpdateHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setString(2,resultId);
            }
         });
      } finally {
         db.release(connection);
      }
      return new ID(resultId);
   }
   
   public boolean goOffline(final ID resourceId)
      throws SQLException
   {
      
      DBConnection connection = db.getConnection();
      try {
         return connection.update(XMPPDB.STATEMENT_GO_OFFLINE, new DBUpdateHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1, id);
               s.setString(2,resourceId.toString());
            }
         })>0;
      } finally {
         db.release(connection);
      }
   }
   
   public Iterator<ID> getResources() 
      throws SQLException
   {
      final Slot<DBIterator<ID>> result = new Slot<DBIterator<ID>>();
      final DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_USER_ONLINE_RESOURCES, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
            }
            public boolean shouldClose() {
               return false;
            }
            public void onResults(ResultSet set)
            {
               result.set(new DBIterator<ID>(set,new DBResultConstructor<ID>() {
                  public ID newInstance(ResultSet set) 
                     throws SQLException
                  {
                     return new ID(set.getString(1));
                  }
               },db,connection));
            }
         });
         return result.get();
      } catch(SQLException ex) {
         db.release(connection);
         throw ex;
      }
   }

   public Iterator<Contact> getContacts() 
      throws SQLException
   {
      final Slot<DBIterator<Contact>> result = new Slot<DBIterator<Contact>>();
      final DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_USER_CONTACTS, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
            }
            public boolean shouldClose() {
               return false;
            }
            public void onResults(ResultSet set)
            {
               result.set(new DBIterator<Contact>(set,new DBResultConstructor<Contact>() {
                  public Contact newInstance(ResultSet set) 
                     throws SQLException
                  {
                     return new Contact(db,set.getInt(1),User.this,new ID(set.getString(2)),Contact.Subscription.fromInt(set.getInt(3)),Contact.Ask.fromInt(set.getInt(4)),set.getString(5));
                  }
               },db,connection));
            }
         });
         return result.get();
      } catch(SQLException ex) {
         db.release(connection);
         throw ex;
      }
   }

   public Iterator<ID> getContactsBySubscription(final Contact.Subscription subscription) 
      throws SQLException
   {
      final Slot<DBIterator<ID>> result = new Slot<DBIterator<ID>>();
      
      final DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_USER_CONTACTS_BY_SUBSCRIPTION, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setInt(2,subscription.getValue());
            }
            public boolean shouldClose() {
               return false;
            }
            public void onResults(ResultSet set)
            {
               result.set(new DBIterator<ID>(set,new DBResultConstructor<ID>() {
                  public ID newInstance(ResultSet set) 
                     throws SQLException
                  {
                     return new ID(set.getString(1));
                  }
               },db,connection));
            }
         });
         return result.get();
      } catch(SQLException ex) {
         db.release(connection);
         throw ex;
      }
   }
   
   public Iterator<ID> getContactsByAsk(final Contact.Ask ask) 
      throws SQLException
   {
      final Slot<DBIterator<ID>> result = new Slot<DBIterator<ID>>();
      
      final DBConnection connection = db.getConnection();
      try {
         connection.query(XMPPDB.STATEMENT_USER_CONTACTS_BY_ASK, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setInt(2,ask.getValue());
            }
            public boolean shouldClose() {
               return false;
            }
            public void onResults(ResultSet set)
            {
               result.set(new DBIterator<ID>(set,new DBResultConstructor<ID>() {
                  public ID newInstance(ResultSet set) 
                     throws SQLException
                  {
                     return new ID(set.getString(1));
                  }
               },db,connection));
            }
         });
         return result.get();
      } catch(SQLException ex) {
         db.release(connection);
         throw ex;
      }
   }
}
